package com.haotian.coder.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.haotian.coder.AiCodeGenTypeRoutingService;
import com.haotian.coder.constant.AppConstant;
import com.haotian.coder.core.AiCodeGeneratorFacade;
import com.haotian.coder.core.builder.VueProjectBuilder;
import com.haotian.coder.core.handler.StreamHandlerExecutor;
import com.haotian.coder.exception.BusinessException;
import com.haotian.coder.exception.ErrorCode;
import com.haotian.coder.exception.ThrowUtils;
import com.haotian.coder.innerService.InnerScreenShotService;
import com.haotian.coder.innerService.InnerUserService;
import com.haotian.coder.mapper.AppMapper;
import com.haotian.coder.model.dto.app.AppAddRequest;
import com.haotian.coder.model.dto.app.AppQueryRequest;
import com.haotian.coder.model.entity.App;
import com.haotian.coder.model.entity.User;
import com.haotian.coder.model.enums.ChatHistoryMessageTypeEnum;
import com.haotian.coder.model.enums.CodeGenTypeEnum;
import com.haotian.coder.model.vo.AppVO;
import com.haotian.coder.model.vo.UserVO;
import com.haotian.coder.service.AppService;
import com.haotian.coder.service.ChatHistoryService;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 应用 服务层实现。
 *
 * @author <a href="https://github.com/liyupi">程序员鱼皮</a>
 */
@Service
@Slf4j
public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {

    @Resource
    private AiCodeGenTypeRoutingService aiCodeGenTypeRoutingService;
    @Resource
    @Lazy
    private InnerUserService userService;
    @Resource
    private AiCodeGeneratorFacade aiCodeGeneratorFacade;
    @Resource
    private ChatHistoryService chatHistoryService;
    @Resource
    private StreamHandlerExecutor streamHandlerExecutor;
    @Resource
    private VueProjectBuilder vueProjectBuilder;
    @Resource
    @Lazy
    private InnerScreenShotService screenShotService;

    @Override
    public boolean removeById(Serializable id) {
        if (id == null) {
            return false;
        }

        long appId = Long.parseLong(id.toString());

        try {
            chatHistoryService.deleteByappId(appId);
        } catch (Exception e) {
            log.error("删除聊天记录失败：{}", e.getMessage());
        }

        return super.removeById(id);
    }    @Override
    public Long createApp(AppAddRequest appAddRequest, User loginUser) {
        ThrowUtils.throwIf(appAddRequest == null, ErrorCode.PARAMS_ERROR);
        // 参数校验
        String initPrompt = appAddRequest.getInitPrompt();
        ThrowUtils.throwIf(StrUtil.isBlank(initPrompt), ErrorCode.PARAMS_ERROR, "初始化 prompt 不能为空");
        // 构造入库对象
        App app = new App();
        BeanUtil.copyProperties(appAddRequest, app);
        app.setUserId(loginUser.getId());
        // 应用名称暂时为 initPrompt 前 12 位
        app.setAppName(initPrompt.substring(0, Math.min(initPrompt.length(), 12)));
        // 暂时设置为Vue生成
        CodeGenTypeEnum codeGenTypeEnum = aiCodeGenTypeRoutingService.routeCodeGenType(initPrompt);
        app.setCodeGenType(codeGenTypeEnum.getValue());
        // 插入数据库
        boolean result = this.save(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "数据库更新失败");

        // 记录日志
        log.info("创建应用成功  appID:{},  类型：{}", app, codeGenTypeEnum);
        return app.getId();
    }


    @Override
    public AppVO getAppVO(App app) {
        if (app == null) {
            return null;
        }
        AppVO appVO = new AppVO();
        BeanUtil.copyProperties(app, appVO);
        // 关联查询用户信息
        Long userId = app.getUserId();
        if (userId != null) {
            User user = userService.getById(userId);
            UserVO userVO = userService.getUserVO(user);
            appVO.setUser(userVO);
        }
        return appVO;
    }


    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        if (appQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = appQueryRequest.getId();
        String appName = appQueryRequest.getAppName();
        String cover = appQueryRequest.getCover();
        String initPrompt = appQueryRequest.getInitPrompt();
        String codeGenType = appQueryRequest.getCodeGenType();
        String deployKey = appQueryRequest.getDeployKey();
        Integer priority = appQueryRequest.getPriority();
        Long userId = appQueryRequest.getUserId();
        String sortField = appQueryRequest.getSortField();
        String sortOrder = appQueryRequest.getSortOrder();
        return QueryWrapper.create().eq("id", id).like("appName", appName).like("cover", cover).like("initPrompt", initPrompt).eq("codeGenType", codeGenType).eq("deployKey", deployKey).eq("priority", priority).eq("userId", userId).orderBy(sortField, "ascend".equals(sortOrder));
    }

    @Override
    public List<AppVO> getAppVOList(List<App> appList) {
        if (CollUtil.isEmpty(appList)) {
            return new ArrayList<>();
        }
        // 批量获取用户信息，避免 N+1 查询问题
        Set<Long> userIds = appList.stream().map(App::getUserId).collect(Collectors.toSet());
        Map<Long, UserVO> userVOMap = userService.listByIds(userIds).stream().collect(Collectors.toMap(User::getId, userService::getUserVO));
        return appList.stream().map(app -> {
            AppVO appVO = getAppVO(app);
            UserVO userVO = userVOMap.get(app.getUserId());
            appVO.setUser(userVO);
            return appVO;
        }).collect(Collectors.toList());
    }


    @Override
    public Flux<String> chatToGenCode(Long appId, String message, User loginUser) {
        // 1. 参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 不能为空");
        ThrowUtils.throwIf(StrUtil.isBlank(message), ErrorCode.PARAMS_ERROR, "用户消息不能为空");
        // 2. 查询应用信息
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // 3. 验证用户是否有权限访问该应用，仅本人可以生成代码
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限访问该应用");
        }
        // 4. 获取应用的代码生成类型
        String codeGenTypeStr = app.getCodeGenType();
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenTypeStr);
        if (codeGenTypeEnum == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "不支持的代码生成类型");
        }

        // 5. 将对话信息保存到数据库中
        chatHistoryService.addChatMessage(appId, message, ChatHistoryMessageTypeEnum.USER.getValue(), loginUser.getId());
        // 6. 调用 AI 生成代码（流式）
        Flux<String> contentStream = aiCodeGeneratorFacade.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
        // 7.选择流式信息处理器，处理对应的 流
        return streamHandlerExecutor.doExecute(contentStream, chatHistoryService, appId, loginUser, codeGenTypeEnum);
    }


    @Override
    public String deployApp(Long appId, User loginUser) {
        // 1.参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 不能为空");
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        // 2. 查询应用信息，是否生成了这个应用
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // 3. 权限校验，只有本人才可以部署网站
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限访问该应用");
        }
        // 4. 检查是否有deployKey，没有key需要随机生成字母加数字的组合
        String deployKey = app.getDeployKey();
        if (StrUtil.isBlank(deployKey)) {
            deployKey = RandomUtil.randomString(6);
        }
        // 5. 获取生成的类型，相关信息
        String codeGenTypeStr = app.getCodeGenType();
        String sourceName = codeGenTypeStr + "_" + appId;
        String sourcePath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceName;
        // 6. 检查资源路径是否存在，将资源复制到路径中
        File sourceFile = new File(sourcePath);
        if (!sourceFile.exists()) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "资源不存在,请先生成应用");
        }

        // vue项目需要特殊处理
        if (codeGenTypeStr.equals(CodeGenTypeEnum.VUE_PROJECT.getValue())) {
            // 构建vue项目
            boolean success = vueProjectBuilder.buildProject(sourcePath);
            ThrowUtils.throwIf(!success, ErrorCode.SYSTEM_ERROR, "构建Vue项目失败");
            // 查询路径是否存在
            File distDir = new File(sourcePath, "dist");
            ThrowUtils.throwIf(!distDir.exists(), ErrorCode.NOT_FOUND_ERROR, "vue项目不存在！");

            // 将distDir作为sourceFile部署
            sourceFile = distDir;
        }
        // 将资源复制到路径中
        String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
        try {
            FileUtil.copyContent(sourceFile, new File(deployDirPath), true);
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文件部署错误！");
        }

        // 7.更新deploykey和生成的时间，更新数据库
        app.setId(appId);
        app.setDeployKey(deployKey);
        app.setDeployedTime(LocalDateTime.now());
        boolean update = this.updateById(app);
        ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "部署应用错误，内部数据更新失败：）");
        // 8. 返回可访问的url
        String url = String.format("%s/%s/", AppConstant.CODE_DEPLOY_HOST, deployKey);

        generateScreenshotAsync(appId, url);
        return url;
    }

    /**
     * 异步生成截图
     *
     * @param appId
     * @param url
     */
    private void generateScreenshotAsync(Long appId, String url) {

        Thread.startVirtualThread(() -> {

            // 生成压缩图片截图，并返回可访问的url
            String pictureUrl = screenShotService.generateAndUploadScreenshot(url);

            // 构造appID入库
            App app = new App();
            app.setId(appId);
            app.setCover(pictureUrl);
            boolean update = this.updateById(app);
            ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "内部数据更新失败：）");
        });
    }


}
